package com.myzl.coal.utils;

import cn.hutool.extra.spring.SpringUtil;
import com.myzl.common.constant.ProtocolConstants;
import com.myzl.common.enums.PlcFunctionEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.List;

import static com.myzl.coal.utils.HexUtil.bytesToHexString;
import static com.myzl.coal.utils.HexUtil.hexStringToByteArray;


/**
 * @author Administrator
 * @date 2022年8月2日11:08:20
 */
@Slf4j
public class PlcCommand {

    /**
     * 读取多个寄存器
     * @param start 寄存器开始地址
     * @param number 读取寄存器数量
     */
    public static byte[] read(Integer deviceId,int start,int number){
        // 部分消息头
        byte[] headerBytes = getHeader(deviceId);
        // 单元标识符，只有一个PLC
        byte[] addressBytes = getOneBytes(1);
        // 功能码03,读保持寄存器，读取单个或多个
        byte[] actionBytes = getOneBytes(PlcFunctionEnum.READ_HOLDING_REGISTERS.getCode());;
        // 读取寄存器数据的开始位置
        byte[] startBytes = getTwoBytes(start);
        // 读取寄存器数量
        byte[] numberBytes = getTwoBytes(number);
        // 长度后续数据
        byte[] nextBytes = HexUtil.concat(addressBytes, actionBytes, startBytes, numberBytes);
        // 报文长度
        byte[] lenBytes = getTwoBytes(nextBytes.length);
        // 完整的消息数据
        byte[] messageBytes = HexUtil.concat(headerBytes,lenBytes, nextBytes);
        String message = bytesToHexString(messageBytes);
        log.debug("read message: {}",message);
        return messageBytes;
    }

    /**
     * 写多个集群器
     * @param fun 写线圈或者寄存器
     * @param start 寄存器开始地址
     * @param number 写取寄存器数量
     * @param dataBytes 数据
     * @return 报文
     */
    public static byte[] write(Integer deviceId,int fun, int start,int number,byte[] dataBytes){
        // 部分消息头
        byte[] headerBytes = getHeader(deviceId);
        // 单元标识符，只有一个PLC
        byte[] addressBytes = getOneBytes(1);
        // 功能码16,写多个保持寄存器
        byte[] actionBytes = getOneBytes(fun);
        // 写寄存器数据的开始位置
        byte[] startBytes = getTwoBytes(start);
        // 写寄存器数量
        byte[] numberBytes = getTwoBytes(number);
        // 数据长度
        byte[] dataLenBytes = getOneBytes(dataBytes.length);
        // 长度后续数据
        byte[] nextBytes = HexUtil.concat(addressBytes, actionBytes, startBytes, numberBytes,dataLenBytes,dataBytes);
        // 报文长度
        byte[] lenBytes = getTwoBytes(nextBytes.length);
        // 完整的消息数据
        byte[] messageBytes = HexUtil.concat(headerBytes,lenBytes, nextBytes);
        String message = bytesToHexString(messageBytes);
        log.debug("write message: {}",message);
        return messageBytes;
    }

    /**
     * 写线圈
     * @param fun 写线圈或者寄存器
     * @param start 寄存器开始地址
     * @param dataBytes 数据
     * @return 报文
     */
    public static byte[] writeCoils(Integer deviceId,int fun, int start,byte[] dataBytes){
        // 部分消息头
        byte[] headerBytes = getHeader(deviceId);
        // 单元标识符，只有一个PLC
        byte[] addressBytes = getOneBytes(1);
        // 功能码05,写线圈
        byte[] actionBytes = getOneBytes(fun);
        // 写线圈的开始位置
        byte[] startBytes = getTwoBytes(start);
        // 长度后续数据
        byte[] nextBytes = HexUtil.concat(addressBytes, actionBytes, startBytes,dataBytes);
        // 报文长度
        byte[] lenBytes = getTwoBytes(nextBytes.length);
        // 完整的消息数据
        byte[] messageBytes = HexUtil.concat(headerBytes,lenBytes, nextBytes);
        String message = bytesToHexString(messageBytes);
        log.debug("write message: {}",message);
        return messageBytes;
    }


    public static byte[] getDataBytes(int data){
        String str = String.format("%04X",data);
        return hexStringToByteArray(str);
    }

    public static byte[] getDataListBytes(List<Integer> data){
        StringBuilder sb = new StringBuilder();
        for (Integer i : data) {
            String str = String.format("%04X",i);
            sb.append(str);
        }
        return hexStringToByteArray(sb.toString());
    }

    public static byte[] getDataListBytesLong(List<Long> data){
        StringBuilder sb = new StringBuilder();
        for (Long i : data) {
            String str;
            if (i>65535){
                str = String.format("%08X",i);
            }else {
                str = String.format("%04X",i);
            }
            sb.append(str);
        }
        return hexStringToByteArray(sb.toString());
    }

    private static byte[] getHeader(Integer deviceId){
        // 事物标识符
//        byte[] txBytes = getTwoBytes(TxId.getNext());
        Long increment = SpringUtil.getBean(StringRedisTemplate.class).opsForHash().increment(ProtocolConstants.DEVICE_CMD_PRIMARY, deviceId.toString(), 1L);
        int l = (int) (increment % 65535);
        byte[] txBytes=getTwoBytes(l);
        // 协议标识符 对于MODBUS 协议来说，这里恒为0
        byte[] signBytes = getTwoBytes(0);
        return HexUtil.concat(txBytes, signBytes);


    }

    private static byte[] getTwoBytes(int i){
        String str = String.format("%04X",i);
        return hexStringToByteArray(str);
    }

    private static byte[] getOneBytes(int i){
        String str = String.format("%02X",i);
        return hexStringToByteArray(str);
    }


}
